﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using TC.SSO.Client.Common;
using System.Web;

namespace TC.SSO.Client
{
    public class WebClient
    {
        /// <summary>
        /// 当前客户端的应用标识
        /// </summary>
        public string AppId { get; protected set; }

        /// <summary>
        /// 当前客户端的应用Key
        /// </summary>
        public string AppKey { get; protected set; }

        /// <summary>
        /// 单点登录服务器的地址
        /// </summary>
        public string Site { get; protected set; }

        protected System.Security.Cryptography.SHA1 _sha1;


        /// <summary>
        /// 获取指定timestamp的签名信息
        /// </summary>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public string Signature(int timestamp)
        {
            return Signature(AppId, AppKey, timestamp);
        }

        /// <summary>
        /// 获取指定参数的签名信息
        /// </summary>
        /// <param name="appid"></param>
        /// <param name="appkey"></param>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public string Signature(string appid, string appkey, int timestamp)
        {
            var paras = new string[] { appid, appkey, timestamp.ToString() };
            Array.Sort(paras);
            byte[] buffer = Encoding.UTF8.GetBytes(string.Join("", paras));
            byte[] result = null;
            try
            {
                result = _sha1.ComputeHash(buffer);
            }
            catch
            {
                _sha1 = System.Security.Cryptography.SHA1.Create();
                result = _sha1.ComputeHash(buffer);
            }
            return BitConverter.ToString(result).Replace("-", "").ToUpper();
        }

        protected string GetFullUrl(string url)
        {
            return string.Format("{0}/{1}", this.Site.TrimEnd('/'), url);
        }
    }

    /// <summary>
    /// 单点Web客户端操作类
    /// </summary>
    public class SSOWebClient : WebClient
    {
        #region 用于与单点服务器交互的URL

        /// <summary>
        /// 新平台的登录页地址
        /// </summary>
        public const string BUILDNET = "auth/buildnet";

        /// <summary>
        /// 工程电讯的登录页地址
        /// </summary>
        public const string GCDX = "auth/gcdx";

        public const string GREENNKIT = "auth/greennkit";
        /// <summary>
        /// 退出登录的页面地址
        /// </summary>
        public const string LOGOUT = "auth/logout";

        /// <summary>
        /// 获取用户信息
        /// </summary>
        public const string USERINFO = "token/getuser";

        /// <summary>
        /// 获取Token状态信息
        /// </summary>
        public const string GETTOKENSTATE = "token/gettokenstate";
        /// <summary>
        /// 获取用户账户信息
        /// </summary>
        public const string GETUSERACCOUNT = "token/getuseraccount";

        /// <summary>
        /// 设置信息
        /// </summary>
        public const string SETMESSAGE = "token/setmessage";

        /// <summary>
        /// 获取最新
        /// </summary>
        public const string GETLAST = "token/getlast";

        /// <summary>
        /// 
        /// </summary>
        const string SESSION_STATE_KEY = "_current_user_site_state_";
        #endregion

        /// <summary>
        /// 状态值
        /// </summary>
        public string State
        {
            get
            {
                return HttpContext.Current.Session[SESSION_STATE_KEY] as string;
            }
            set
            {
                HttpContext.Current.Session[SESSION_STATE_KEY] = value;
            }
        }

        Random _rd = new Random();

        #region 单例
        static SSOWebClient _current;

        static SSOWebClient()
        {
            _current = new SSOWebClient();
        }
        private SSOWebClient()
            : this(ConfigurationManager.AppSettings["AppId"], ConfigurationManager.AppSettings["AppKey"], ConfigurationManager.AppSettings["SSO.WebSite"])
        {
        }

        private readonly Tcbci.Logging.Core.Logger _log = null;
        private SSOWebClient(string appid, string appkey, string site)
        {
            Site = site;
            AppId = appid;
            AppKey = appkey;
            _sha1 = System.Security.Cryptography.SHA1.Create();
            _log = Tcbci.Logging.Core.LoggerManager.LoggerFactory.GetLogger(this.GetType());
        }
        #endregion

        public static SSOWebClient Current
        {
            get
            {
                return _current;
            }
            private set
            {
                _current = value;
            }
        }

        /// <summary>
        /// 使用指定的参数创建一个新的客户端操作类
        /// </summary>
        /// <param name="appid"></param>
        /// <param name="appkey"></param>
        /// <param name="site"></param>
        /// <returns></returns>
        public static SSOWebClient Create(string appid, string appkey, string site)
        {
            return new SSOWebClient(appid, appkey, site);
        }

        #region 常用操作

        #region 用户信息
        private string GetRandomString(int length)
        {
            byte[] bytes = new byte[Math.Max(5, length)];
            _rd.NextBytes(bytes);
            StringBuilder sb = new StringBuilder();
            foreach (byte b in bytes)
            {
                sb.Append(b.ToString());
            }

            return sb.ToString();
        }

        /// <summary>
        /// 登录到默认页（buildnet）
        /// </summary>
        /// <param name="redirect"></param>
        public void Login(string redirect)
        {
            Login(BUILDNET, redirect);
        }

        /// <summary>
        /// 登录到指定网站
        /// 登录成功后会返回到指定的回调地址并附加一个token参数，为了确保该地址的安全，用户可在回调地址中添加一个随机参数，并自行判断，以确定该地址的合法性
        /// </summary>
        /// <param name="loginUrl">单点登录的地址</param>
        /// <param name="redirect">登录成功后的回调地址</param>
        /// <param name="generateState">是否自动添加回调的随机数，对于调用v2方法，此值恒为true</param>
        public void Login(string loginUrl, string redirect, bool generateState = false)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string url = string.Empty;
            if (generateState)
            {
                this.State = GetRandomString(5);
                url = string.Format("{0}?redirect={1}&state={2}", GetFullUrl(loginUrl),
                    System.Web.HttpUtility.UrlEncode(redirect), this.State);
            }
            else
            {
                url = string.Format("{0}?redirect={1}", GetFullUrl(loginUrl),
                    System.Web.HttpUtility.UrlEncode(redirect));
            }
            _log.Debug("Login ===> Url:{0},Redirect:{1},this.State:{2}", HttpContext.Current.Request.Url.ToString(), url, this.State);
            //Ocor.Logger.LogWriter.Write(Ocor.Logger.LogLevel.Debug, "Login", string.Format("Url:{0},Redirect:{1},this.State:{2}", HttpContext.Current.Request.Url.ToString(), url, this.State));
            HttpContext.Current.Response.Redirect(url, false);
        }

        /// <summary>
        /// 登录到默认页（buildnet）
        /// 不同于Login，此方法会自动在redirect中加入state参数
        /// </summary>
        /// <param name="redirect"></param>
        public void LoginV2(string redirect)
        {
            LoginV2(BUILDNET, redirect);
        }

        /// <summary>
        /// 登录到指定网站
        /// 登录成功后会返回到指定的回调地址并附加一个token参数，该方法已在redirect中自动添加state参数以确保调用URL的合法性
        /// 推荐使用此方法进行登录
        /// </summary>
        /// <param name="loginUrl">单点登录的地址</param>
        /// <param name="redirect">登录成功后的回调地址</param>
        public void LoginV2(string loginUrl, string redirect)
        {
            redirect = GetRedirectWhithState(redirect);
            Login(loginUrl, redirect, true);
        }

        /// <summary>
        /// 生成本地凭据
        /// 调用些方法将会检查请求中的token以及state，如果参数无误，将调用generate生成本地凭据，如果生成凭据失败，请返回false
        /// 如果参数有误或生成失败并且用户将autoRedirect设置为true时此方法会自动将用户重定向到指定的登录地址
        /// </summary>
        /// <param name="generate">生成本地凭据的回调方法</param>
        /// <param name="autoRedirect">是否在验证失败时自动执行重定向</param>
        public void GenerateLocalCredential(Func<string, bool> generate, bool autoRedirect = true)
        {
            GenerateLocalCredential(BUILDNET, generate, autoRedirect);
        }

        /// <summary>
        /// 生成本地凭据
        /// 调用些方法将会检查请求中的token以及state，如果参数无误，将调用generate生成本地凭据，如果生成凭据失败，请返回false
        /// 如果参数有误或生成失败并且用户将autoRedirect设置为true时此方法会自动将用户重定向到指定的登录地址
        /// 推荐在进行本地凭据生成时调用此方法
        /// </summary>
        /// <param name="loginUrl">参数错误或生成失败时重定向的登录地址</param>
        /// <param name="generate">生成本地凭据的回调方法</param>
        /// <param name="autoRedirect">是否在验证失败时自动执行重定向</param>
        public void GenerateLocalCredential(string loginUrl, Func<string, bool> generate, bool autoRedirect)
        {
            GenerateLocalCredential(loginUrl, (state) =>
            {
                //Ocor.Logger.LogWriter.Write(Ocor.Logger.LogLevel.Debug, "GenerateLocalCredential", string.Format("Url:{0},this.State:{1}", HttpContext.Current.Request.Url.ToString(), this.State));
                _log.Debug("GenerateLocalCredential ===> Url:{0},this.State:{1}", HttpContext.Current.Request.Url.ToString(), this.State);
                return !string.IsNullOrEmpty(state) && this.State == state;
            }, generate, autoRedirect);
        }

        /// <summary>
        /// 生成本地凭据
        /// 调用此方法将会调用validate检查请求参数是合法，如果无误，将调用generate生成本地凭据，如果生成凭据失败，请返回false
        /// 如果参数有误或生成失败并且用户将autoRedirect设置为true时此方法会自动将用户重定向到指定的登录地址
        /// </summary>
        /// <param name="loginUrl">参数错误或生成失败时重定向的登录地址</param>
        /// <param name="validate">验证请求参数是否合法</param>
        /// <param name="generate">生成本地凭据的回调方法</param>
        /// <param name="autoRedirect">是否在验证失败时自动执行重定向</param>
        public void GenerateLocalCredential(string loginUrl, Func<string, bool> validate, Func<string, bool> generate, bool autoRedirect)
        {
            var rq = HttpContext.Current.Request;
            string token = rq.QueryString["token"] ?? "",
                   state = rq.QueryString["state"] ?? "",
                   notremove = rq.QueryString["once"] ?? "";
            if (validate(state))
            {
                if (generate(token))
                {
                    if (!string.IsNullOrWhiteSpace(notremove))
                        return;
                    //移除token及state参数
                    HttpContext.Current.Response.Redirect(GetRedirectRemoveTokenState(rq.Url.ToString()));
                    return;
                }
            }
            if (autoRedirect)
            {
                string redirect = rq.Url.ToString();
                LoginV2(loginUrl, redirect);
            }
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="token"></param>
        /// <param name="redirect"></param>
        public void Logout(string token, string redirect)
        {
            Logout(token, BUILDNET, redirect);
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="token">令牌</param>
        /// <param name="logoutUrl">单点登录的地址</param>
        /// <param name="redirect">跳转的URL</param>
        public void Logout(string token, string loginUrl, string redirect)
        {
            //组装redirect参数
            redirect = string.Format("/{0}?redirect={1}", loginUrl, System.Web.HttpUtility.UrlEncode(redirect));

            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string url = string.Format("{0}?redirect={1}", GetFullUrl(LOGOUT),
                System.Web.HttpUtility.UrlEncode(redirect));
            HttpContext.Current.Response.Redirect(url, true);
        }

        /// <summary>
        /// 退出操作
        /// 用户调用此方法将进行系统的退出操作，如果验证参数无误，此方法会调用单点服务的退出页以进行令牌的注销工作
        /// </summary>
        /// <param name="remoteToken">退出登录时，单点服务会回调系统的退出页面，并在URL中附加Token值，此Token既为remoteToken</param>
        /// <param name="localToken">本地系统保存的Token</param>
        /// <param name="loginUrl">单点登录的地址</param>
        /// <param name="redirect">登录成功后回跳的URL</param>
        /// <param name="clearSessionAction">清空Session的操作，系统可调用此方法以清除用户在本地系统保留的凭据，从而将用户置为未登录状态</param>
        /// <param name="message">验证失败时的错误信息</param>
        /// <returns>退出是否成功</returns>
        public bool Logout(string remoteToken, string localToken, string loginUrl, string redirect, Action clearSessionAction, out string message)
        {
            message = "操作成功";
            if (string.IsNullOrEmpty(localToken))
            {
                clearSessionAction.Invoke();
                Logout(localToken, loginUrl, redirect);
                return true;
            }
            if (string.IsNullOrEmpty(remoteToken))
            {
                message = "操作非法，remoteToken为空";
                return false;
            }
            if (localToken != remoteToken)
            {
                message = "操作非法，remoteToken与localToken不相等";
                return false;
            }
            clearSessionAction.Invoke();
            Logout(localToken, loginUrl, redirect);
            return true;
        }

        /// <summary>
        /// 退出操作，此操作会退出到默认的登录页（BuildNet）
        /// 用户调用此方法将进行系统的退出操作，如果验证参数无误，此方法会调用单点服务的退出页以进行令牌的注销工作
        /// </summary>
        /// <param name="remoteToken">退出登录时，单点服务会回调系统的退出页面，并在URL中附加Token值，此Token既为remoteToken</param>
        /// <param name="localToken">本地系统保存的Token</param>
        /// <param name="redirect">登录成功后回跳的URL</param>
        /// <param name="clearSessionAction">清空Session的操作，系统可调用此方法以清除用户在本地系统保留的凭据，从而将用户置为未登录状态</param>
        /// <param name="message">验证失败时的错误信息</param>
        /// <returns>退出是否成功</returns>
        public bool Logout(string remoteToken, string localToken, string redirect, Action clearSessionAction, out string message)
        {
            return Logout(remoteToken, localToken, BUILDNET, redirect, clearSessionAction, out message);
        }

        /// <summary>
        /// 退出登录
        /// 不同于Logout，此方法会自动在redirect中加入state参数
        /// </summary>
        /// <param name="token"></param>
        /// <param name="redirect"></param>
        public void LogoutV2(string token, string redirect)
        {
            LogoutV2(token, BUILDNET, redirect);
        }

        /// <summary>
        /// 退出登录
        /// 不同于Logout，此方法会自动在redirect中加入state参数
        /// </summary>
        /// <param name="token">令牌</param>
        /// <param name="logoutUrl">单点登录的地址</param>
        /// <param name="redirect">跳转的URL</param>
        public void LogoutV2(string token, string loginUrl, string redirect)
        {
            redirect = GetRedirectWhithState(redirect);
            Logout(token, loginUrl, redirect);
        }

        /// <summary>
        /// 拼接一个state参数
        /// </summary>
        /// <param name="redirect"></param>
        /// <returns></returns>
        string GetRedirectWhithState(string redirect)
        {
            //this.State = Guid.NewGuid().ToString("N");
            redirect = System.Text.RegularExpressions.Regex.Replace(redirect, @"&?state=([^&]*)", "")
                       .TrimEnd('?').TrimEnd('&');
            //if (redirect.IndexOf("?") != -1)
            //{
            //    redirect = redirect + "&state=" + this.State;
            //}
            //else
            //{
            //    redirect = redirect + "?state=" + this.State;
            //}
            //_log.Debug("Step2:CombineURL_{0},State:{1}", HttpContext.Current.Request.Url.ToString(), this.State);
            return redirect;
        }

        /// <summary>
        /// 返回移除token和state的URL
        /// </summary>
        /// <param name="redirect"></param>
        /// <returns></returns>
        string GetRedirectRemoveTokenState(string redirect)
        {
            redirect = System.Text.RegularExpressions.Regex.Replace(redirect, @"&?state=([^&]*)", "")
                       .TrimEnd('?').TrimEnd('&');
            redirect = System.Text.RegularExpressions.Regex.Replace(redirect, @"&?token=([^&]*)", "")
                       .TrimEnd('?').TrimEnd('&');
            return redirect;
        }

        /// <summary>
        /// 退出操作
        /// 用户调用此方法将进行系统的退出操作，如果验证参数无误，此方法会调用单点服务的退出页以进行令牌的注销工作
        /// 不同于Logout，此方法会自动在redirect中加入state参数
        /// </summary>
        /// <param name="remoteToken">退出登录时，单点服务会回调系统的退出页面，并在URL中附加Token值，此Token既为remoteToken</param>
        /// <param name="localToken">本地系统保存的Token</param>
        /// <param name="loginUrl">单点登录的地址</param>
        /// <param name="redirect">登录成功后回跳的URL</param>
        /// <param name="clearSessionAction">清空Session的操作，系统可调用此方法以清除用户在本地系统保留的凭据，从而将用户置为未登录状态</param>
        /// <param name="message">验证失败时的错误信息</param>
        /// <returns>退出是否成功</returns>
        public bool LogoutV2(string remoteToken, string localToken, string loginUrl, string redirect, Action clearSessionAction, out string message)
        {
            message = "操作成功";
            if (string.IsNullOrEmpty(localToken))
            {
                clearSessionAction.Invoke();
                Logout(localToken, loginUrl, redirect);
                return true;
            }
            if (string.IsNullOrEmpty(remoteToken))
            {
                message = "操作非法，remoteToken为空";
                return false;
            }
            if (localToken != remoteToken)
            {
                message = "操作非法，remoteToken与localToken不相等";
                return false;
            }
            clearSessionAction.Invoke();
            redirect = GetRedirectWhithState(redirect);
            Logout(localToken, loginUrl, redirect);
            return true;
        }

        /// <summary>
        /// 退出操作，此操作会退出到默认的登录页（BuildNet）
        /// 用户调用此方法将进行系统的退出操作，如果验证参数无误，此方法会调用单点服务的退出页以进行令牌的注销工作
        /// 不同于Logout，此方法会自动在redirect中加入state参数
        /// </summary>
        /// <param name="remoteToken">退出登录时，单点服务会回调系统的退出页面，并在URL中附加Token值，此Token既为remoteToken</param>
        /// <param name="localToken">本地系统保存的Token</param>
        /// <param name="redirect">登录成功后回跳的URL</param>
        /// <param name="clearSessionAction">清空Session的操作，系统可调用此方法以清除用户在本地系统保留的凭据，从而将用户置为未登录状态</param>
        /// <param name="message">验证失败时的错误信息</param>
        /// <returns>退出是否成功</returns>
        public bool LogoutV2(string remoteToken, string localToken, string redirect, Action clearSessionAction, out string message)
        {
            return LogoutV2(remoteToken, localToken, BUILDNET, redirect, clearSessionAction, out message);
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="token">令牌</param>
        /// <returns></returns>
        public OperRult<UserInfo> GetUserInfo(string token)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string url = string.Format("{0}?appid={1}&timestamp={2}&sign={3}&token={4}", GetFullUrl(USERINFO), AppId, timestamp, Signature(timestamp), token);
            using (var rsp = TC.SSO.Client.Common.HttpUtility.CreateHttpGetResponse(url))
            {
                using (var sr = new System.IO.StreamReader(rsp.GetResponseStream(), Encoding.UTF8))
                {
                    string result = sr.ReadToEnd();
                    return Tools.JsonDeserializer<OperRult<UserInfo>>(result);
                }
            }
        }

        /// <summary>
        /// 获取用户账户信息
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public OperRult<UserFundsAccount[]> GetUserAccount(string token)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string url = string.Format("{0}?appid={1}&timestamp={2}&sign={3}&token={4}&debug=1", GetFullUrl(GETUSERACCOUNT), AppId, timestamp, Signature(timestamp), token);
            using (var rsp = TC.SSO.Client.Common.HttpUtility.CreateHttpGetResponse(url))
            {
                using (var sr = new System.IO.StreamReader(rsp.GetResponseStream(), Encoding.UTF8))
                {
                    string result = sr.ReadToEnd();
                    return Tools.JsonDeserializer<OperRult<UserFundsAccount[]>>(result);
                }
            }
        }

        /// <summary>
        /// 检查Token的状态值
        /// </summary>
        /// <param name="token">令牌</param>
        /// <returns></returns>
        public OperRult<int> GetTokenState(string token)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string url = string.Format("{0}?appid={1}&timestamp={2}&sign={3}&token={4}", GetFullUrl(GETTOKENSTATE), AppId, timestamp, Signature(timestamp), token);
            using (var rsp = TC.SSO.Client.Common.HttpUtility.CreateHttpGetResponse(url))
            {
                using (var sr = new System.IO.StreamReader(rsp.GetResponseStream(), Encoding.UTF8))
                {
                    string result = sr.ReadToEnd();
                    return Tools.JsonDeserializer<OperRult<int>>(result);
                }
            }
        }

        #endregion

        #region 消息中心

        public OperRult<int> SetMessage(int from, int to, string title, string content, int type)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            var msg = new Message
            {
                FromUser = from,
                ToUser = to,
                Title = title,
                Content = content,
                Type = type
            };
            string paras = string.Format("from={0}&to={1}&title={2}&content={3}&type={4}", from, to, System.Web.HttpUtility.UrlEncode(title), System.Web.HttpUtility.UrlEncode(content), type);
            string url = string.Format("{0}?appid={1}&timestamp={2}&sign={3}&{4}", GetFullUrl(SETMESSAGE), AppId, timestamp, Signature(timestamp), paras);
            using (var rsp = TC.SSO.Client.Common.HttpUtility.CreateHttpPostResponse(url, Common.Tools.JsonSerializer(msg)))
            {
                using (var sr = new System.IO.StreamReader(rsp.GetResponseStream(), Encoding.UTF8))
                {
                    string result = sr.ReadToEnd();
                    return Tools.JsonDeserializer<OperRult<int>>(result);
                }
            }
        }

        public OperRult<ListResult> GetLast(int uid, int last)
        {
            int timestamp = Tools.ConvertDateTimeInt(DateTime.Now);
            string paras = string.Format("uid={0}&last={1}", uid, last);
            string url = string.Format("{0}?appid={1}&timestamp={2}&sign={3}&{4}", GetFullUrl(GETLAST), AppId, timestamp, Signature(timestamp), paras);
            using (var rsp = TC.SSO.Client.Common.HttpUtility.CreateHttpGetResponse(url))
            {
                using (var sr = new System.IO.StreamReader(rsp.GetResponseStream(), Encoding.UTF8))
                {
                    string result = sr.ReadToEnd();
                    return Tools.JsonDeserializer<OperRult<ListResult>>(result);
                }
            }
        }
        #endregion

        #endregion
    }

    /// <summary>
    /// 模型层返回的结果对象
    /// </summary>
    public class OperRult<T>
    {
        public bool Success { get; set; }

        public string Message { get; set; }

        public T Value { get; set; }
    }

    public class UserAccountToken
    {
        /// <summary>
        /// 令牌，用户交互信息
        /// </summary>
        public string Token { get; set; }

        /// <summary>
        /// 用户可以用此令牌对重新申请一个新令牌而无需用户名和密码
        /// </summary>
        public string TokenPair { get; set; }

        public int CreateDate { get; set; }

        public int ExpirsDate { get; set; }
    }

    public class UserInfo
    {
        public int UserId { get; set; }

        public string UserName { get; set; }

        public string UserTitle { get; set; }

        public string UserTrueName { get; set; }

        public string UserEmail { get; set; }

        public int LastLanguage { get; set; }

        public string UserPhoto1 { get; set; }

        public string UserPhoto2 { get; set; }

        public string UserPhoto3 { get; set; }

        public int UserGroupType { get; set; }

        public string NickName { get; set; }

        public int NewPlat { get; set; }
    }

    /// <summary>
    /// 用户账户 
    /// </summary>
    public class UserFundsAccount
    {
        /// <summary>
        /// 账户ID
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 用户ID
        /// </summary>
        public int UserID { get; set; }

        /// <summary>
        /// 账户类型 1.积分 2.金币 3.人民币
        /// </summary>
        public int Type { get; set; }

        /// <summary>
        /// 账户总额
        /// </summary>
        public decimal Total { get; set; }

        /// <summary>
        /// 开户时间
        /// </summary>
        public DateTime OpenDate { get; set; }

    }

    public class ListResult
    {
        public int Last { get; set; }

        public Message[] Value { get; set; }
    }

    /// <summary>
    /// 站内消息
    /// </summary>
    public class Message
    {
        /// <summary>
        /// 发送人
        /// </summary>
        public int FromUser { get; set; }

        /// <summary>
        /// 收件人
        /// </summary>
        public int ToUser { get; set; }

        /// <summary>
        /// 消息类型
        /// </summary>
        [Obsolete]
        public int Type { get; set; }

        /// <summary>
        /// 消息标题
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 消息内容
        /// </summary>
        public string Content { get; set; }
    }
}
